using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Caliburn.Micro;

namespace Exones.Blend.AssetsManager
{
	class BlendFolder : PropertyChangedBase
	{
		private string name;
		private IObservableCollection<BlendFolder> subFolders;
		private bool? isChecked = false;
		private BlendFolder parentFolder;
		private bool notifyChecked = true;
		private bool isThreeState = false;
		private bool isAssetsHolder = false;
		private string registryPath;
		private bool isExpanded;
		

		internal string RegistryPath
		{
			get { return registryPath; }
			set { registryPath = value; }
		}

		public virtual bool IsLeaf
		{
			get { return false; }
		}

		public bool IsAssetsHolder
		{
			get { return isAssetsHolder; }
			set { isAssetsHolder = value; }
		}

		public BlendFolder ParentFolder
		{
			get { return parentFolder; }
			set { parentFolder = value; }
		}

		public IObservableCollection<BlendFolder> SubFolders
		{
			get { return subFolders; }
		}

		public bool IsThreeState
		{
			get { return isThreeState; }
			set
			{
				if (value == isThreeState)
					return;

				isThreeState = value;
				NotifyOfPropertyChange(() => IsThreeState);
			}
		}

		private bool isSelected;
		private bool isSelectable = true;

		public bool IsSelected
		{
			get { return isSelected; }
			set
			{
				isSelected = value;
				NotifyOfPropertyChange(() => IsSelected);
			}
		}

		public bool? IsChecked
		{
			get { return isChecked; }
			set
			{
				if (value == isChecked)
					return;

				isChecked = value;

				if (isChecked != null)
				{
					IsThreeState = false;
				}

				if (notifyChecked)
				{
					UpdateChildrenCheckedState(isChecked);
					UpdateParentCheckedState(isChecked);
					((ShellViewModel)IoC.Get<IShellViewModel>()).NotifyOfPropertyChange("CanInstallAssets");
					NotifyOfPropertyChange(() => IsChecked);
				}
			}
		}

		internal IEnumerable<BlendFolder> TraverseChildren()
		{
			Stack<BlendFolder> stack = new Stack<BlendFolder>();

			stack.Push(this);

			while (stack.Count > 0)
			{
				var current = stack.Pop();

				foreach (var folder in current.SubFolders)
				{
					stack.Push(folder);
					yield return folder;
				}
			}
		}

		private void UpdateParentCheckedState(bool? isChecked)
		{
			var current = ParentFolder;

			while (current != null)
			{
				var subTree = current.TraverseChildren().ToList();
				var allChecked = subTree.All(folder => folder.IsChecked == true);
				var allUnchecked = subTree.All(folder => folder.IsChecked == false);

				notifyChecked = false;
				if (allChecked)
				{
					current.IsThreeState = false;
					current.IsChecked = true;
				}
				else if (allUnchecked)
				{
					current.IsThreeState = false;
					current.IsChecked = false;
				}
				else
				{
					current.IsThreeState = true;
					current.IsChecked = null;
				}
				notifyChecked = true;
				current.NotifyOfPropertyChange(() => IsChecked);

				current = current.ParentFolder;
			}
		}

		private void UpdateChildrenCheckedState(bool? isChecked)
		{
			if (isChecked == null)
				return;

			foreach (var folder in subFolders)
			{
				folder.IsChecked = isChecked;
			}
		}

		public bool IsSelectable
		{
			get
			{
				return isSelectable;
			}
			set
			{
				isSelectable = value; 
				NotifyOfPropertyChange(() => IsSelectable);
			}
		}

		public string Name
		{
			get { return name; }
			set
			{
				name = value;
				NotifyOfPropertyChange(() => Name);
			}
		}

		public bool IsExpanded
		{
			get { return isExpanded; }
			set
			{
				isExpanded = value;
				NotifyOfPropertyChange(() => IsExpanded);
			}
		}


		public BlendFolder(string name): this(name, Enumerable.Empty<BlendFolder>())
		{
		}

		public BlendFolder(string name, IEnumerable<BlendFolder> subFolders)
		{
			if (name == null) throw new ArgumentNullException("name");

			this.name = name;
			this.subFolders = new BindableCollection<BlendFolder>(subFolders);

			foreach (var subFolder in this.subFolders)
			{
				subFolder.ParentFolder = this;
			}
		}

		public override string ToString()
		{
			return Name;
		}
	}
}